package org.asciidoc.intellij.psi;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderEx;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class AsciiDocFileUtil {
  public static final Key<CachedValue<ProjectSectionCache>> KEY_ASCIIDOC_SECTIONS_IN_PROJECT = new Key<>("asciidoc-sections-in-project");

  public static ProjectSectionCache getProjectSectionCache(Project project) {
    CachedValue<ProjectSectionCache> cache = CachedValuesManager.getManager(project).createCachedValue(
      () -> CachedValueProvider.Result.create(new ProjectSectionCache(), PsiModificationTracker.MODIFICATION_COUNT));
    return ((UserDataHolderEx) project).putUserDataIfAbsent(KEY_ASCIIDOC_SECTIONS_IN_PROJECT, cache).getValue();
  }

  public static List<AsciiDocSection> findSections(Project project, String key) {
    if (key.length() == 0) {
      return Collections.emptyList();
    }
    ProjectSectionCache cache = getProjectSectionCache(project);
    List<AsciiDocSection> result = cache.get(key);
    if (result != null) {
      return result;
    }
    final GlobalSearchScope scope = new AsciiDocSearchScope(project).restrictedByAsciiDocFileType();
    Collection<AsciiDocSection> asciiDocSections = new ArrayList<>();
    asciiDocSections.addAll(AsciiDocSectionKeyIndex.getInstance().get(key, project, scope));
    asciiDocSections.addAll(AsciiDocSectionKeyIndex.getInstance().get(AsciiDocSectionStubElementType.SECTION_WITH_VAR, project, scope));
    for (AsciiDocSection asciiDocSection : asciiDocSections) {
      if (asciiDocSection.matchesTitle(key) || (asciiDocSection.matchesAutogeneratedId(key) && asciiDocSection.getBlockId() == null)) {
        if (result == null) {
          result = new ArrayList<>();
        }
        result.add(asciiDocSection);
      }
    }
    if (result == null) {
      result = Collections.emptyList();
    }
    result = Collections.unmodifiableList(result);
    cache.put(key, result);
    return result;
  }

  public static List<AsciiDocSection> findSections(Project project) {
    List<AsciiDocSection> result = new ArrayList<>();
    final GlobalSearchScope scope = new AsciiDocSearchScope(project).restrictedByAsciiDocFileType();
    AsciiDocSectionKeyIndex.getInstance().processAllElements(project,
      asciiDocSection -> {
        result.add(asciiDocSection);
        return true;
      }, scope);
    return result;
  }
}
